﻿# coding=utf-8
#-------------------------------------------------------------------------------
# Name:        locations
# Purpose:
#
# Author:      GaiveR
#
# Created:     24.08.2011
# Copyright:   (c) ASM-Team 2011
# Licence:     private
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import os
import random
import pygame
import transform
from utils import Timer
from mapworker import MapWorker
from monsters import *
from towers import *

# actions panel
import actionsgui

# tkinter for open file dialog
import tkinter as tk
from tkinter.filedialog import askopenfilename


################################################################################

class Location():
    """
    Базовый класс для всех локаций. Обрабатывает общие события
    У каждой локации свои обработчики событий, апдейта и перерисовки.
    При желании можно будет раскидать по разным файлам
    """
    def __init__(self, game):
        self.game = game
        self.screen = pygame.display.get_surface()
        self.font = pygame.font.SysFont('Consolas', 14) # default font
        self.timer = Timer()

    def events(self, events):
        #обработка общих событий: выход и пр
        for event in events:
            if event.type == pygame.QUIT:
                self.game.quit()

    def draw(self): pass
    def update(self, time_pass): pass

################################################################################


class MenuLocation(Location):
    """
    Меню. Выбор настроек, переход к игре
    """

    MENU_PLAY, MENU_SELECT_MAP, MENU_EXIT = range(3)
    MENU_LAST = MENU_EXIT

    def __init__(self, game):
        Location.__init__(self, game)
        self.font = pygame.font.SysFont('Consolas', 20) # custom font
        self.arrow = pygame.image.load(os.path.join(IMAGES_PATH, 'arrow.png')).convert()

        self.choise = self.MENU_PLAY
        self.menu_items = {
            self.MENU_PLAY: ['Play', self.game.showGame, None],  # label, func, rect
            self.MENU_SELECT_MAP: ['Select map...', self.selectMap, None],
            self.MENU_EXIT: ['Exit', self.game.quit, None]
        }

        # for open file dialog
        root = tk.Tk()
        root.withdraw() # show askopenfilename dialog without the tkinter window


    def activate(self):
        """Call the method associated with the current menu item
        """
        self.menu_items[self.choise][1]()

    def selectMap(self):
        file_name = askopenfilename(title = "Выбираем карту",
            initialdir = MAPS_PATH,
            filetypes = [('Tower defense map', '*.tdmap')])
        if (file_name):
            self.game.currentMapPath = file_name
            self.game.game_location.running = False

    def events(self, events):
        Location.events(self, events)
        for event in events:
            if event.type == pygame.KEYUP:
                if event.key == 13:
                    self.activate()
                elif event.key == pygame.K_ESCAPE:
                    self.game.quit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_DOWN:
                    self.choise += 1
                    if self.choise > len(self.menu_items) - 1:
                        self.choise = len(self.menu_items) - 1
                elif event.key == pygame.K_UP:
                    self.choise -= 1
                    if self.choise < 0: self.choise = 0

            elif event.type == pygame.MOUSEMOTION:
                pos = pygame.mouse.get_pos()
                try:
                    # мыша выше первого пункта - выбрать первый
                    if pos[1] < self.menu_items[0][2][1]:  # y < menu_item[0].y
                        self.choise = 0
                    # мыша ниже последнего пункта - выбрать последний
                    elif pos[1] > self.menu_items[self.MENU_LAST][2][1] + self.menu_items[self.MENU_LAST][2][3]: # y > menu_item[last].y
                        self.choise = self.MENU_LAST
                    else:
                        # выбрать пункт меню, на уровне которого находится мыша
                        for key, item in self.menu_items.items():
                            if item[2][1] <= pos[1] <= item[2][1] + item[2][3]:
                                self.choise = key
                except TypeError:
                    pass # None[index]

            elif event.type == pygame.MOUSEBUTTONUP:
                if event.button == MOUSE_LEFT:
                    self.activate()


    def draw(self):
        self.screen.fill((0, 0, 0))
        if pygame.font.get_init():
            dy = 20
            x = transform.SCREEN_WIDTH / 2 - 50
            y = transform.SCREEN_HEIGHT / 2 - len(self.menu_items) * dy / 2
            for key in self.menu_items:
                text = self.menu_items[key][0]
                item_surface = self.font.render(text, True, pygame.Color('white'))
                item_rect = item_surface.get_rect(topleft=(x, y))

                self.menu_items[key][2] = item_rect
                self.screen.blit(item_surface, (x, y))
                if key == self.choise:
                    self.screen.blit(self.arrow,
                                    (x - self.arrow.get_width() - 5, y))

                y += dy


################################################################################


class GameLocation(Location):
    """
    Собственно, игра.
    """

    def __init__(self, game):
        Location.__init__(self, game)
        self.monsters = pygame.sprite.Group()
        self.towers = pygame.sprite.Group()
        self.bullets = pygame.sprite.Group()
        self.building = None
        self.running = False
        self.money = 0  # real init in goMapWorker()
        ################
        actionsgui.initApp(self.screen, self)
        ##actionsgui.app.showTowerMenu()


    def events(self, events):
        Location.events(self, events)
        for event in events:
            # нажатия клавиш
            if event.type == pygame.KEYUP:
                if event.key == pygame.K_ESCAPE:
                    self.cancelAction()
                elif event.key == pygame.K_s:
                    self.newMonsters(random.randrange(1, COUNT_MONSTER_CLASSES+1))
                elif event.key == pygame.K_t:
                    self.newTowerBegin(random.randrange(1, COUNT_TOWER_CLASSES+1))
                elif event.key == pygame.K_m:
                    actionsgui.app.showMainMenu()
                elif event.key == pygame.K_y:
                    actionsgui.app.showTowerMenu()
                # внимание! дальше идет быдлокод
                elif event.key == pygame.K_u:
                    self.newTowerBegin(1)
                elif event.key == pygame.K_i:
                    self.newTowerBegin(2)
                elif event.key == pygame.K_o:
                    self.newTowerBegin(3)
                elif event.key == pygame.K_p:
                    self.newTowerBegin(4)

            # события мыши
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == MOUSE_RIGHT:
                    self.cancelAction()
            elif event.type == pygame.MOUSEBUTTONUP:
                if event.button == MOUSE_LEFT:
                    if self._isGameAreaClick(event.pos): # кликнули в игре, не на панельке действий
                        if self.building: self.newTowerEnd()

            elif event.type == pygame.MOUSEMOTION:
                if self.building:
                    pos = transform.getRelativePosition(event.pos, self.canvas_pos)
                    self.building.x = pos[0]
                    self.building.y = pos[1]

            # user events
            elif event.type == OTHER_EVENT:
                if event.adv_type == EVENT_KILL_MONSTER:
                    self.money += event.monster.cost
                elif event.adv_type == EVENT_GAME_OVER:
                    print('Game over')

            actionsgui.app.event(event) # для GUIшки

    def update(self, time_pass):
        self.monsters.update(time_pass)
        self.towers.update(time_pass)
        self.bullets.update(time_pass)
        self.timer.update(time_pass)

        # двигаем мапу как в стратежках
        x, y = pygame.mouse.get_pos()
        border_width = 7
        speed = 5
        # мыша влево
        if x < border_width:
            self.canvas_pos[0] += speed
            if self.canvas_pos[0] > 0: self.canvas_pos[0] = 0
        # мыша направо
        if transform.SCREEN_WIDTH - x < border_width:
            self.canvas_pos[0] -= speed
            if self.canvas_pos[0] < self.screen.get_width() - self.canvas.get_width():
                self.canvas_pos[0] = self.screen.get_width() - self.canvas.get_width()
        # мыша вверх
        if y < border_width:
            self.canvas_pos[1] += speed
            if self.canvas_pos[1] > 0: self.canvas_pos[1] = 0
        # мыша вниз
        if transform.SCREEN_HEIGHT - y < border_width:
            self.canvas_pos[1] -= speed
            if self.canvas_pos[1] < self.getGameHeight() - self.canvas.get_height():
                self.canvas_pos[1] = self.getGameHeight() - self.canvas.get_height()
##            if self.canvas_pos[1] < self.screen.get_height() - self.canvas.get_height():
##                self.canvas_pos[1] = self.screen.get_height() - self.canvas.get_height()

    def draw(self, draw_grid=False):

        def render(disp, rect):

            def drawStatistic(canvas):
                    """Draw current statistic info: money, wave, next wave time
                    """
                    color = pygame.Color('black')
                    text = 'money: {0} wave: {1}\\{2}'.format(self.money,
                        2, 4)
                    text_surface = self.font.render(text, True, color)
                    canvas.blit(text_surface, (10, 10))

            self.canvas.blit(self.background, (0, 0))
            # grid
            if draw_grid:
                color = (0, 0, 0) # black
                x = 0
                while x < self.canvas.get_width():
                    pygame.draw.line(self.canvas, color, (x, 0), (x, self.canvas.get_height()))
                    x += transform.scaleX
                y = 0
                while y < self.canvas.get_height():
                    pygame.draw.line(self.canvas, color, (0, y), (self.canvas.get_width(), y))
                    y += transform.scaleY

            # game objects
            self.monsters.draw(self.canvas)
            self.towers.draw(self.canvas)
            self.bullets.draw(self.canvas)

            if self.building:
##              pos = pygame.mouse.get_pos()
##              pos = (pos[0] - self.canvas_pos[0], pos[1] - self.canvas_pos[1])
                self.building.draw(self.canvas)

            # draw canvas on the screen
            self.screen.blit(self.canvas, self.canvas_pos)
            drawStatistic(self.screen)

            return (rect,)
            ##

        # draw code there
        #actionsgui.app.paint() # вынести в game.showMenu()
        updates = []
        rect = actionsgui.app.get_render_area()
        self.screen.set_clip(rect)
        lst = render(self.screen, rect)
        if lst:
            updates += lst
        self.screen.set_clip()
        lst = actionsgui.app.update()
        if lst:
            updates += lst
        pygame.display.update(updates)

    def getGameHeight(self):
        #return transform.SCREEN_HEIGHT - actionsgui.app.menuAreaHeight
        return actionsgui.app.gameAreaHeight

    def getGameWidtht(self):
        return transform.SCREEN_WIDTH

    def cancelAction(self):
        if self.building:
            self.building = None
        else:
            self.game.showMenu()

    def goMapWorker(self, map_):
        self.map_worker = MapWorker(map_)
        self.money = self.map_worker.start_money
        for spawn_time, (nbr_monsters, monster_id) in sorted(self.map_worker.spawns.items()):
            self.timer.add(int(spawn_time) * 1000, self.monstersFabric(monster_id, nbr_monsters), 1)

        # load background
        background_path = os.path.join(MAPS_PATH, self.map_worker.background)
        self.background = pygame.image.load(background_path).convert()

        # load map_bmp to compute scaleX and scaleY
        map_bmp_path = os.path.join(MAPS_PATH, self.map_worker.map_bmp)
        map_bmp = pygame.image.load(map_bmp_path).convert()

        if self.map_worker.map_size[0] < transform.SCREEN_WIDTH:
            transform.SCREEN_WIDTH = self.map_worker.map_size[0]
        if self.map_worker.map_size[1] < transform.SCREEN_HEIGHT:
            transform.SCREEN_HEIGHT = self.map_worker.map_size[1]
        pygame.display.set_mode((transform.SCREEN_WIDTH, transform.SCREEN_HEIGHT))

        transform.scaleX = self.map_worker.map_size[0] / map_bmp.get_width()
        transform.scaleY = self.map_worker.map_size[1] / map_bmp.get_height()
        self.background = pygame.transform.scale(self.background,
                                                (self.map_worker.map_size[0],
                                                 self.map_worker.map_size[1]))
        self.canvas = pygame.surface.Surface(self.map_worker.map_size)  # на ней будем все рисовать
        self.canvas_pos = [0, 0]  # и потом отображать на screen в таких координатах


    def monstersFabric(self, monster_id, count = -1):
        """
        Передаем количество монстров count, получаем функцию создания
        count монстров. По умолчанию count = количеству spawn_points
        """
        def createMonsters():
            size = (int(transform.scaleX), int(transform.scaleY))
            if count < 0:
                for position in self.map_worker.spawn_points:
                    self.monsters.add(MonsterFabric.create(monster_id, position, self.map_worker))
            else:
                spoints_count = len(self.map_worker.spawn_points)
                cicles = count // spoints_count
                iters = count % spoints_count
                for i in range(cicles):
                    for position in self.map_worker.spawn_points:
                        self.monsters.add(MonsterFabric.create(monster_id, position, self.map_worker))
                for i in range(iters):
                    ##self.monsters.add(MonsterFabric.create(id, random.choice(self.map_worker.spawn_points), self.map_worker))
                    self.monsters.add(MonsterFabric.create(monster_id, self.map_worker.spawn_points[i], self.map_worker))
        return createMonsters

    def newMonsters(self, monster_id, count = -1):
        """
        Вызов newMonsters без фабрики
        """
        self.monstersFabric(monster_id, count)()

    def newTowerBegin(self, tower_id):
        pos = transform.getRelativePosition(pygame.mouse.get_pos(), self.canvas_pos)
        ##self.building = TowerArrow(pos, self.map_worker, self)
        self.building = TowerFabric.create(tower_id, pos, self.map_worker, self)

    def newTowerEnd(self):
            ## no need more
##        def buildingEndFabric(building):
##            def buildingEnd():
##                building.activate()
##            return buildingEnd

        if self.money < self.building.cost:
            print('Need more money')
            return

        ##pos = self.building.x, self.building.y
        pos = transform.getRelativePosition(pygame.mouse.get_pos(), self.canvas_pos)
        self.building.x = pos[0]
        self.building.y = pos[1]
        if self.map_worker.canBuild(pos, self.building):
            self.towers.add(self.building)
            self.map_worker.addToMap(self.building)
            self.building.startBuild()
            self.timer.add(self.building.build_time * 1000, self.building.endBuild, 1)
            self.money -= self.building.cost
            self.building = None
        else:
           print("Can't build there")

    def _isGameAreaClick(self, pos):
        """
        Нажатие в пределах игровой области, не меню
        """
        return actionsgui.app.gameArea.rect.collidepoint(pos)

################################################################################
